﻿// -
// <copyright file="SpeechGrammersClient.cs" company="Microsoft Corporation">
//    Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// -

namespace Hawaii.Services.Speech.Recognition
{
    using System;
    using System.Net;
    using System.Collections.Generic;
    using Hawaii.Services.Client;
    using System.Threading;
    using System.Xml;
    using System.IO;
    using System.Text;

    /// <summary>
    /// SpeechGrammarsClient is a helper class for contacting Hawaii Speech Service
    /// to receive all available/loaded grammars in the server. Currently, only the 
    /// 'Dictation' grammar is available for context free speech recognition.
    /// </summary>
    public partial class SpeechGrammarsClient : BaseClientProxy
    {
        private const string ServiceSignature = "SpeechRecognition";
        private const string Grammar = "Grammar";
        private const string DeviceIdName = "DeviceId";

        /// <summary>
        /// A delegate signature for the speech grammars received event.
        /// </summary>
        /// <param name="sender">Specifies the sender object.</param>
        /// <param name="e">Specifies the event arguments.</param>
        public delegate void SpeechGrammarsReceivedEventHandler(object sender, SpeechGrammarsReceivedEventArgs e);

        /// <summary>
        /// [in] Specifies the host name in which the speech service is running.
        /// For example: If the speech service is running in http://stt.hawaii-services.net, the host name
        /// will be 'stt.hawaii-services.net'.
        /// </summary>
        public string HostName { get; set; }

        /// <summary>
        /// [in] Specifies the mobile client identification for speech recognition.
        /// </summary>
        public string DeviceId { get; set; }

        /// <summary>
        /// [out] Specifies the grammar names received from the service.
        /// </summary>
        private List<string> Grammars { get; set; }

        /// <summary>
        /// Specifies the speech grammars received event.
        /// </summary>
        public event SpeechGrammarsReceivedEventHandler SpeechGrammarsReceived;

        /// <summary>
        /// Default constructor
        /// </summary>
        public SpeechGrammarsClient() :
            base(HttpMethod.Get)
        {
            this.HostName = string.Empty;
            this.DeviceId = string.Empty;
            this.Grammars = null;
        }

        /// <summary>
        /// Over loaded constructor with hostName and deviceId arguments.
        /// </summary>
        /// <param name="hostName">Specifies the host name of the speech service.</param>
        /// <param name="deviceId">Specifies the mobile client device id.</param>
        public SpeechGrammarsClient(string hostName, string deviceId) :
            base(HttpMethod.Get)
        {
            this.HostName = hostName;
            this.DeviceId = deviceId;
            this.Grammars = null;
        }

        /// <summary>
        /// An overridden abstract method to create the speech service uri object.
        /// This method will be called by the base class when it tries to connect 
        /// to the server during a speech recognition service call.
        /// </summary>
        /// <returns></returns>
        protected override Uri ServiceUri()
        {
            // -
            // A service call cannot continue without a valid device id.
            // -
            if (string.IsNullOrEmpty(this.DeviceId))
            {
                throw new Exception("DeviceId is null or empty");
            }

            // -
            // Create the service Uri.
            // 
            ServiceUri uri = new ServiceUri(this.HostName, SpeechGrammarsClient.ServiceSignature);
            uri.AddQueryString(SpeechGrammarsClient.DeviceIdName, Uri.EscapeUriString(this.DeviceId));

            // -
            // Return the URI object.
            // -
            return new Uri(uri.ToString());
        }

        /// <summary>
        /// An overridden abstract method that gets the POST data. 
        /// This method will be called by the base class when it needs data 
        /// during a Http POST method call.
        /// </summary>
        /// <returns></returns>
        protected override byte[] GetPostData()
        {
            // -
            // As this service call is a Http GET call, no input data is allowed.
            // -
            throw new NotImplementedException();
        }

        /// <summary>
        /// An overridden abstract method used to parse the result from the
        /// speech service.
        /// </summary>
        /// <param name="responseStream"></param>
        protected override void ParseOutput(Stream responseStream)
        {
            // -
            // The following is the output response from the server
            //
            // <Grammars deviceId="12345">
            //      <Grammar name="Dictation" />
            //      <Grammar name="PizzaOrdering" />
            // </Grammars>
            // -
            List<string> domains = new List<string>();
            using (XmlReader reader = XmlReader.Create(responseStream))
            {
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        // -
                        // Check for the <Grammar/> element.
                        // -
                        if (reader.Name.Equals(SpeechGrammarsClient.Grammar, StringComparison.OrdinalIgnoreCase))
                        {
                            while (reader.MoveToNextAttribute())
                            {
                                // -
                                // Read the value of the 'name' attribute.
                                // -
                                domains.Add(reader.Value);
                            }
                        }
                    }
                }
            }

            if(domains.Count != 0)
            {
                this.Grammars = domains;
            }
        }

        /// <summary>
        /// Method to invoke the get speech grammars asynchronously.
        /// </summary>
        public void GetSpeechGrmmarsAsync()
        {
            // -
            // Call the speech recognition service in another thread from the
            // thread pool.
            // - 
            ThreadPool.QueueUserWorkItem(GetSpeechGrammarsThreadFunc, this);
        }

        /// <summary>
        /// Thread function for receiving speech grammars. It is called by a seperate thread 
        /// from the thread pool during an asynchronous service call.
        /// </summary>
        /// <param name="stateInfo"></param>
        private void GetSpeechGrammarsThreadFunc(object stateInfo)
        {
            SpeechGrammarsClient client = stateInfo as SpeechGrammarsClient;
            // -
            // Call the speech service to recognize speech.
            // -
            bool result = client.ProcessRequest();

            // -
            // Call all client event handling methods.
            // -
            if (client.SpeechGrammarsReceived != null)
            {
                SpeechGrammarsReceivedEventArgs eventArgs = null;
                if (result)
                {
                    // - 
                    // A successful service call
                    // -
                    eventArgs = new SpeechGrammarsReceivedEventArgs(this.Grammars);
                }
                else
                {
                    // -
                    // On error, set the exception.
                    // -
                    eventArgs = new SpeechGrammarsReceivedEventArgs(this.ErrorException);
                }

                this.SpeechGrammarsReceived(client, eventArgs);
            }
        }
    }
}
